package com.flyduck.system.service;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.flyduck.common.config.FlyduckConfig;
import com.flyduck.common.constant.RedisKeyConstant;
import com.flyduck.common.exception.BusinessException;
import com.flyduck.common.util.*;
import com.flyduck.common.vo.LoginInfoVO;
import com.flyduck.common.vo.Result;
import com.flyduck.common.vo.RouteVO;
import com.flyduck.common.vo.UserInfoVO;
import com.flyduck.system.dao.SysMenuDao;
import com.flyduck.system.dao.SysRoleMenuDao;
import com.flyduck.system.dao.SysUserDao;
import com.flyduck.system.dao.SysUserRoleDao;
import com.flyduck.system.entity.SysMenuEntity;
import com.flyduck.system.entity.SysUserEntity;
import com.flyduck.system.enums.MenuTypeEnum;
import com.flyduck.system.enums.UserTypeEnum;
import com.flyduck.system.exception.SystemExceptionCode;
import com.flyduck.system.vo.menu.trans.SysMenuEntityToRouteVO;
import com.flyduck.system.vo.user.trans.SysUserEntityToUserInfoVO;
import com.flyduck.system.vo.login.LoginReqVO;
import com.google.common.collect.Lists;
import com.wf.captcha.SpecCaptcha;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @program: flyduck-platform2
 * @description:
 * @author: flyduck
 * @create: 2021-04-23 16:05
 **/
@Service
public class LoginService {
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private FlyduckConfig flyDuckConfig;
    @Autowired
    private TokenUtil tokenUtil;
    @Autowired
    private SysUserDao sysUserDao;
    @Autowired
    private SysMenuDao sysMenuDao;
    @Autowired
    private SysUserRoleDao sysUserRoleDao;
    @Autowired
    private SysRoleMenuDao sysRoleMenuDao;


    public Map<String,String> getCode(){
        SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 5);
        String verifyCode = specCaptcha.text().toLowerCase();
        String key = RedisKeyConstant.getRedisKeyPrefix(RedisKeyConstant.CAPTCHA_CODE_KEY);
        redisUtil.setCacheObject(key,verifyCode,flyDuckConfig.getCode().getExpireTime(), TimeUnit.MINUTES);
        Map<String,String> resultMap =  new HashMap<>();
        resultMap.put("uuid", key);
        resultMap.put("img", specCaptcha.toBase64());
        return resultMap;
    }


    public String login(@RequestBody LoginReqVO reqVO){
        //1.校验验证码
        String value = redisUtil.getCacheObject(reqVO.getUuid());
        if(StringUtil.isBlank(value) || !value.equals(reqVO.getCode().toLowerCase())){
            throw new BusinessException(SystemExceptionCode.CAPTCHA_EXPIRED);
        }
        //2.从数据库查询用户
        LoginInfoVO loginInfo = getLoginInfo(reqVO.getUserName());
        if(ObjectUtil.isNull(loginInfo)){
            throw new BusinessException(SystemExceptionCode.USER_NOT_FOUND);
        }
        //3.比对密码
        boolean isMatch = false;
        isMatch = RSAUtil.check(reqVO.getPassword(),loginInfo.getUserInfo().getPassword());
        if(!isMatch){
            throw new BusinessException(SystemExceptionCode.PASSWORD_ERROR);
        }
        //4.发放token
        String token = RedisKeyConstant.getRedisKeyPrefix(RedisKeyConstant.LOGIN_TOKEN_KEY);
        //5.存储token(uuid)到redis   uuid--->用户信息    前台传token,后台去redis里面取出来，取不到就重新认证
        String userInfo = JSON.toJSONString(loginInfo);
        redisUtil.setCacheObject(token,userInfo,flyDuckConfig.getToken().getExpireTime(),TimeUnit.MINUTES);
        return token;
    }

    public Result login(){
        return Result.success( tokenUtil.getLoginInfo());
    }


    private LoginInfoVO getLoginInfo(String userName){
        //************************* 1.获取用户基本信息 *************************
        SysUserEntity user = sysUserDao.getUserByUserName(userName);
        if(ObjectUtil.isNull(user)){
            return null;
        }
        UserInfoVO userInfoVO = new SysUserEntityToUserInfoVO().apply(user);
        //************************* 2.获取菜单树 *************************
        List<SysMenuEntity> menuEntityList = Lists.newArrayList();
        if(UserTypeEnum.SYS_USER.getValue().equals(user.getUserType())){
            menuEntityList = sysMenuDao.getAllMenus();
        }else {
            //根据用户id获取角色id集合
            List<Long> roleIds = sysUserRoleDao.getRoleIdsByUserId(user.getId());
            //根据角色id集合获取菜单id集合
            List<Long> menuIds = sysRoleMenuDao.getMenuIdsByRoleIds(roleIds);
            //根据菜单id集合获取菜单集合
            menuEntityList = sysMenuDao.selectMenusByIds(menuIds);
        }
        //************************* 3.获取可以访问的url *************************
        List<String> urls = menuEntityList.stream()
                .map(SysMenuEntity::getUrl)
                .filter(item -> StringUtil.isNotBlank(item))
                .distinct()
                .collect(Collectors.toList());
        //筛选出目录和菜单
        menuEntityList = menuEntityList.stream()
                .filter(sysMenuEntity -> !MenuTypeEnum.BUTTON.getValue().equals(sysMenuEntity.getMenuType()))
                .collect(Collectors.toList());
        List<RouteVO> routeVOS = Lists.transform(menuEntityList, new SysMenuEntityToRouteVO());
        //构建菜单树
        List<RouteVO> routes = null;
        routes = TreeUtil.toTree(RouteVO.class, routeVOS);
        //************************* 4.构造返回数据 *************************
        LoginInfoVO loginInfoVO = new LoginInfoVO();
        loginInfoVO.setUserInfo(userInfoVO);
        loginInfoVO.setUrls(urls);
        loginInfoVO.setRoutes(routes);
        return loginInfoVO;
    }
}
